package ua.pp.bizon.yandex.api.impl;

import ua.pp.bizon.yandex.api.IHTTPUtils;
import ua.pp.bizon.yandex.api.IHttpRemoteUtils;
import ua.pp.bizon.yandex.api.IUserData;
import ua.pp.bizon.yandex.api.IUserDataManager;
import ua.pp.bizon.yandex.api.ObjectLookup;
import ua.pp.bizon.yandex.api.WindowManager;
import ua.pp.bizon.yandex.uploader.atom.AppLink;
import ua.pp.bizon.yandex.uploader.remote.RemoteAlbumFactory;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URI;
import java.util.Arrays;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpException;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.HttpResponseException;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.utils.URIUtils;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.DefaultHttpClient;

public class HTTPUtils implements IHTTPUtils, IHttpRemoteUtils {

	private static Log log = LogFactory.getLog(HTTPUtils.class);

	/*
	 * (non-Javadoc)
	 * 
	 * @see ua.pp.bizon.yandex.api.impl.IHttpRemoteUtils#get(java.lang.String,
	 * java.lang.String)
	 */
	@Override
	public String get(String url, String token) throws HttpResponseException {
		do {
			HttpClient httpclient = new DefaultHttpClient();
			try {
				HttpGet httpget = new HttpGet(url);
				log.trace("executing request " + httpget.getURI());
				if (token != null) {
					httpget.addHeader("Authorization", "OAuth " + token);
				}
				ResponseHandler<String> responseHandler = new BasicResponseHandler();
				HttpHost target = determineTarget(httpget);
				HttpResponse response = httpclient.execute(target, httpget);
				log.trace("response headers: " + Arrays.asList(response.getAllHeaders()));
				String responseBody = responseHandler.handleResponse(response);
				if (response.getFirstHeader("Content-Type").getValue().contains("xml")) {
					log.trace("response: " + responseBody);
				}
				return responseBody;
			} catch (HttpResponseException e) {
				log.warn(e.getMessage(), e);
				if (e.getStatusCode() == 401) {
					ObjectLookup.lookup(WindowManager.class).login(true);
				} else {
					throw e;
				}
			} catch (IOException e) {
				log.warn(e.getMessage(), e);
			} finally {
				httpclient.getConnectionManager().shutdown();
			}
		} while (true);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * ua.pp.bizon.yandex.api.impl.IHttpRemoteUtils#post(org.apache.http.HttpEntity
	 * , ua.pp.bizon.yandex.uploader.atom.AppLink, java.lang.String,
	 * org.apache.http.Header[])
	 */
	@Override
	public String post(HttpEntity fileEntity, AppLink to, String contentType, Header[] headers) throws HttpException {
		IUserData defaultEnv = ObjectLookup.lookup(IUserDataManager.class).get(false);
		String token = defaultEnv == null ? null : defaultEnv.getToken();
		HttpClient httpclient = new DefaultHttpClient();
		String link = to.getLink();
		HttpPost httppost = new HttpPost(link);
		HttpResponse response = null;
		ResponseHandler<String> responseHandler = new BasicResponseHandler();
		if (headers != null) {
			for (Header h : headers) {
				httppost.addHeader(h);
			}
		}
		if (token != null) {
			httppost.addHeader("Authorization", "OAuth " + token);
		}
		httppost.addHeader("Content-Type", contentType);
		httppost.setEntity(fileEntity);
		try {
			HttpHost target = determineTarget(httppost);
			log.trace(httppost.getRequestLine());
			BufferedReader reader = new BufferedReader(new InputStreamReader(fileEntity.getContent()));
			log.trace("-------------------Start entity --------------------");
			String buffer = null;
			if (token != null && contentType.contains("xml")) {
				while ((buffer = reader.readLine()) != null) {
					log.trace(buffer);
				}
			}
			log.trace("------------------- End  entity --------------------");
			response = httpclient.execute(target, httppost);
			if (response.getStatusLine().getStatusCode() == 302) {
				String correctLink = response.getFirstHeader("Location").getValue();
				to.setLink(correctLink);
				return post(fileEntity, to, contentType, headers);
			}
			if (response.getStatusLine().getStatusCode() > 299) {
				log.fatal(response.getStatusLine());
				for (Header h : response.getAllHeaders()) {
					log.fatal(h.getName() + ": " + h.getValue());
				}
				response.getEntity().writeTo(System.out);
			}
			return responseHandler.handleResponse(response);
		} catch (IOException e) {
			log.error(e.getMessage(), e);
			throw new HttpException(e.getMessage(), e);
		}
	}

	private static HttpHost determineTarget(HttpUriRequest request) throws ClientProtocolException {
		// A null target may be acceptable if there is a default target.
		// Otherwise, the null target is detected in the director.
		HttpHost target = null;

		URI requestURI = request.getURI();
		if (requestURI.isAbsolute()) {
			target = URIUtils.extractHost(requestURI);
			if (target == null) {
				throw new ClientProtocolException("URI does not specify a valid host name: " + requestURI);
			}
		}
		return target;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see ua.pp.bizon.yandex.api.impl.IHttpRemoteUtils#put(java.lang.String,
	 * ua.pp.bizon.yandex.uploader.atom.AppLink)
	 */
	@Override
	public String put(String entity, AppLink to) throws HttpException {
		String token = ObjectLookup.lookup(IUserDataManager.class).get().getToken();
		HttpClient httpclient = new DefaultHttpClient();
		String link = to.getLink();
		HttpPut httppost = new HttpPut(link);
		HttpResponse response = null;
		ResponseHandler<String> responseHandler = new BasicResponseHandler();
		httppost.addHeader("Authorization", "OAuth " + token);
		httppost.addHeader("Content-Type", RemoteAlbumFactory.CONTENT_TYPE_ALBUM);
		try {
			httppost.setEntity(new StringEntity(entity));
			HttpHost target = determineTarget(httppost);
			log.trace(httppost.getRequestLine());
			log.trace("-------------------Start entity --------------------");
			log.trace(entity);
			log.trace("------------------- End  entity --------------------");
			response = httpclient.execute(target, httppost);
			if (response.getStatusLine().getStatusCode() > 299) {
				log.fatal(response.getStatusLine());
				for (Header h : response.getAllHeaders()) {
					log.fatal(h.getName() + ": " + h.getValue());
				}
				response.getEntity().writeTo(System.out);
			}
			return responseHandler.handleResponse(response);
		} catch (IOException e) {
			log.error(e.getMessage(), e);
			throw new HttpException(e.getMessage(), e);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * ua.pp.bizon.yandex.api.impl.IHttpRemoteUtils#remove(ua.pp.bizon.yandex
	 * .uploader.atom.AppLink)
	 */
	@Override
	public boolean remove(AppLink url) throws HttpException {
		String token = ObjectLookup.lookup(IUserDataManager.class).get().getToken();
		HttpClient httpclient = new DefaultHttpClient();
		HttpDelete httppost = new HttpDelete(url.getLink());
		HttpResponse response = null;
		httppost.addHeader("Authorization", "OAuth " + token);
		try {
			HttpHost target = determineTarget(httppost);
			log.trace(httppost.getRequestLine());
			response = httpclient.execute(target, httppost);
			if (response.getStatusLine().getStatusCode() == 204) {
				return true;
			}
			if (response.getStatusLine().getStatusCode() > 299) {
				log.fatal(response.getStatusLine());
				for (Header h : response.getAllHeaders()) {
					log.fatal(h.getName() + ": " + h.getValue());
				}
				response.getEntity().writeTo(System.out);
			}
			return false;
		} catch (IOException e) {
			log.error(e.getMessage(), e);
			throw new HttpException(e.getMessage(), e);
		}
	}
}
